package com.cw.demo.im.server;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;


import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

/**
 *
 * 该注解是一个类级别的注解，它的作用就是将当前类定义为一个websocket的服务器端，value的值表示访问路径。
 * 将该类注册到spring容器中，个人理解这里并没有用到分层，所以不必使用@controller等分化后的注解
 *
 * @author: wyh
 * @description:websocket服务端点，处理具体逻辑
 * @date: 12/22/2018 15:09
 */
@ServerEndpoint(value = "/myWebsocket/{userid}")
@Component
public class MySocketServer {

    private Session session;
    //记录当前连接，每个客户端都有对应的session，服务器端通过它向客户端发送消息
    //private static Map<String,Session> sessionPool = new HashMap<String,Session>();//存储所有userid及对应的session
    //private static Map<String,String> sessionIds = new HashMap<String,String>();//存储所有的sessionid和userid
    //因为HashMap是线程不安全的，所以我们使用concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MySocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    private static CopyOnWriteArraySet<Map<String,Session>> userInfos = new CopyOnWriteArraySet<>();
    //泛型就是当前类
    private static int onLineNum = 0;
    //静态变量，记录当前在线连接数，所有对在线数的操作都应加上synchhronized关键字以确保线程安全
    /**
     * @param session
     * @param userid
     * @return void
     * @author wyh
     * @description 有新的链接建立成功时调用该方法
     * 该注解表示当有新用户连接时调用该方法
     **/
    @OnOpen
    public void open(Session session, @PathParam(value = "userid") String userid) {
        //@PathParam(value="userid")表示从访问路径上获取参数赋值给当前形参,value的值与路径上的名字一致
        this.session = session;
        //获取当前session
        webSocketSet.add(this);
        Map<String,Session> map = new HashMap<>();
        map.put(userid,session);
        userInfos.add(map);
        //将当前session加入到set中
        addOnlineNum();//在线数增加
    }

    /**
     * @param message
     * @return void
     * @author wyh
     * @description 接收到消息时调用该方法
     * //该注解表示当服务器端收到客户端发送的消息时调用该方法
     **/
    @OnMessage
    public void onMessage(String message) {

        System.out.println("当前发送人sessionid为" + session.getId() + ",发送内容为：" + message);
    }

    /**
     * @param
     * @return void
     * @author wyh
     * @description 断开连接时调用该方法
     **/
    @OnClose//该注解表示当前用户连接断开时调用该方法
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineNum();//在线数减1
    }

    /**
     * @param session
     * @param error
     * @return void
     * @author wyh
     * @description 出现错误时调用
     **/
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误：" + error.getMessage() + ",sessionid为" + session.getId());
        error.printStackTrace();
    }

    /**
     * @param message
     * @return void
     * @author wyh
     * @description 服务器端主动发送消息
     **/
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote()
            .sendText(message);
        System.out.println("发总给指定客户端，sessionid为：" + this.session.getId() + "消息为：" + message);
    }

    /**
     * @param message
     * @return void
     * @author wyh
     * @description 群发消息
     **/
    public void sendMessages(String message) {
        for (MySocketServer s : webSocketSet) {
            try {
                s.session.getBasicRemote()
                    .sendText(message);
                System.out.println("群发给sessionid为：" + s.session.getId() + "的客户端,消息为：" + message);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    /**
     * @param message
     * @return void
     * @author wyh
     * @description 群发消息
     **/
    public void sendMessagesToUser(String userId,String message) {
        for (Map<String,Session> map : userInfos) {
            try {
                Session session = map.get(userId);
                if(session != null){
                    session.getBasicRemote()
                        .sendText(message);
                    System.out.println("群发给sessionid为：" + session.getId() + "的客户端,消息为：" + message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    /**
     * @param
     * @return int
     * @author wyh
     * @description 获取当前在线数, 使用关键字
     **/
    public synchronized int getOnlineNum() {
        return onLineNum;
    }

    /**
     * @param
     * @return void
     * @author wyh
     * @description 在线数加1，注意关键字
     **/
    public static synchronized void addOnlineNum() {
        onLineNum++;
    }

    /**
     * @param
     * @return void
     * @author wyh
     * @description 在线数减1
     **/
    public static synchronized void subOnlineNum() {
        onLineNum--;
    }
}
